//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_MEMORY_FILE_H
#define _UXS_MEMORY_FILE_H

#pragma once

#include "BaseFile.h"

namespace UX_STL
{
namespace File
{
	
	class BaseMemRFile;
	class BaseMemWFile;
	class MemRFile;
	class MemWFile;

	
	//
	// Base Memory Read only File
	//

	class BaseMemRFile : public RFile
	{
	// types
	private:
		typedef uni_array< uint8 >	_mem_t;


	// variables
	protected:
		binary_array_t	_memBuffer;
		_mem_t			_mem;
		isize			_pos;
		bool			_opened;


	// methods
	public:
		BaseMemRFile () : _pos(0), _opened(false)	{}
		virtual ~BaseMemRFile ()					{ _Close(); }


		// RFile //
		virtual isize ReadBuf (void * buf, usize size) override
		{
			if ( not _opened )
				return -1;

			if ( _pos + size > _mem.Count() )
				size = _mem.Count() - _pos;

			MemCopy( buf, _mem.ptr() + _pos, size );
			_pos += size;

			return size;
		}


		// BaseFile //
		virtual void Close () override
		{
			return _Close();
		}

		virtual bool IsOpened () override
		{
			return _opened;
		}
		
		virtual bool SeekSet (usize offset) override
		{
			return _SetPos( offset );
		}

		virtual bool SeekCur (isize offset) override
		{
			return _SetPos( _pos + offset );
		}

		virtual bool SeekEnd (usize offset) override
		{
			return _SetPos( _mem.Count() - offset );
		}

		virtual usize RemainingSize () const override
		{
			return _mem.Count() - _pos;
		}

		virtual usize Size () const override
		{
			return _mem.Count();
		}

		virtual usize Pos () const override
		{
			return _pos;
		}
		
		virtual usize IsEOF () const override
		{
			return Pos() >= Size();
		}

		virtual uni_c_string	Name () const override
		{
			return uni_c_string();
		}

		virtual EFile::type		GetType () const override
		{
			return EFile::MEMORY;
		}


	protected:
		void _Close ()
		{
			_memBuffer.ClearMem();
			_mem	= _memBuffer;
			_pos	= 0;
			_opened = false;
		}
		
	private:
		bool _SetPos (isize newPos)
		{
			if ( newPos >= 0 and
				 newPos < (isize)_mem.Count() )
			{
				_pos = newPos;
				return true;
			}
			else
			{
				_pos = 0;
				return false;
			}
		}
	};

	

	//
	// Memory Read only File
	//

	class MemRFile : public BaseMemRFile
	{
	// type
	public:
		SHARED_POINTER( MemRFile );

		enum EFlag
		{
			NONE = 0,
			COPY = 1,
			MOVE = 2,
		};


	// methods
	public:
		MemRFile () {}

		
		static MemRFilePtr New ()
		{
			return new MemRFile();
		}


		static MemRFilePtr New (const RFilePtr &file)
		{
			MemRFilePtr	mfile = new MemRFile();

			if ( mfile->Create( file ) )
				return mfile;

			return null;
		}

		template <typename T>
		static MemRFilePtr New (const uni_array<const T> arr, EFlag flag)
		{
			MemRFilePtr file = new MemRFile();

			if ( file->Create( arr, flag ) )
				return file;

			return null;
		}


		bool Create (const RFilePtr &file)
		{
			CHECK_ERR( this != file.ptr() );

			usize	size = file->RemainingSize();
			
			_pos	= 0;
			_opened = true;

			_memBuffer.Resize( size, false );
			_mem	= _memBuffer;

			CHECK_ERR( file->Read( _mem.ptr(), _mem.Count() ) );
			return true;
		}

		
		template <typename T>
		bool Create (const uni_array<const T> arr, EFlag flag)
		{
			CHECK_ERR( not arr.Empty() );

			switch ( flag )
			{
				case COPY :
					_memBuffer.Copy( (uint8 *)arr.ptr(), arr.Size() );
					_mem	= _memBuffer;
					break;

				case NONE :
					_memBuffer.Clear();
					_mem	= arr;
					break;

				case MOVE :
					RETURN_ERR( "move operation not supported for this array type" );

				default :
					RETURN_ERR( "invalid flag" );
			}

			_pos	= 0;
			_opened	= true;

			return true;
		}

		
		bool CreateFromArray (INOUT binary_array_t &arr, EFlag flag)
		{
			CHECK_ERR( not arr.Empty() );
			
			_memBuffer.ClearMem();

			switch ( flag )
			{
				case COPY :
					_memBuffer	= arr;
					_mem		= _memBuffer;
					break;

				case NONE :
					_mem	= arr;
					break;

				case MOVE :
					_memBuffer.Clear();
					MemMover::Swap( _memBuffer, arr );
					_mem	= _memBuffer;
					break;

				default :
					RETURN_ERR( "invalid flag" );
			}

			_pos	= 0;
			_opened	= true;

			return true;
		}


		bool CreateFromMemWFile (const SHARED_POINTER_TYPE( BaseMemWFile ) &file, EFlag flag);
		

		uni_array<const uint8> GetData () const
		{
			return (uni_array<const uint8>) _mem;
		}
	};



	//
	// Base Memory Write only File
	//
	
	class BaseMemWFile : public WFile
	{
		friend class MemRFile;

	// variables
	protected:
		binary_array_t	_mem;
		isize			_pos;
		bool			_opened;


	// methods
	public:
		BaseMemWFile () : _pos(0), _opened(false)	{}
		virtual ~BaseMemWFile ()					{ _Close(); }


		// WFile //
		virtual isize WriteBuf (const void * buf, usize size) override
		{
			if ( not _opened )
				return -1;

			if ( _pos != isize(_mem.Count()) )
			{
				_mem.SetCount( _pos );
			}
			
			_mem.Append( uni_array<const uint8>::FromVoid( buf, size ) );
			_pos += size;

			return size;
		}
		
		virtual void Flush () override
		{
		}

		
		// BaseFile //
		virtual void Close () override
		{
			return _Close();
		}

		virtual bool IsOpened () override
		{
			return _opened;
		}
		
		virtual bool SeekSet (usize offset) override
		{
			return _SetPos( offset );
		}

		virtual bool SeekCur (isize offset) override
		{
			return _SetPos( _pos + offset );
		}

		virtual bool SeekEnd (usize offset) override
		{
			return _SetPos( _mem.Count() - offset );
		}

		virtual usize RemainingSize () const override
		{
			return _mem.Count() - _pos;
		}

		virtual usize Size () const override
		{
			return _mem.Count();
		}

		virtual usize Pos () const override
		{
			return _pos;
		}
		
		virtual usize IsEOF () const override
		{
			return Pos() >= Size();
		}

		virtual uni_c_string	Name () const override
		{
			return uni_c_string();
		}

		virtual EFile::type		GetType () const override
		{
			return EFile::MEMORY;
		}


	protected:
		void _Close ()
		{
			_mem.ClearMem();
			_pos = 0;
			_opened = false;
		}
		
	private:
		bool _SetPos (isize newPos)
		{
			if ( newPos >= 0 and
				 newPos < (isize)_mem.Count() )
			{
				_pos = newPos;
				return true;
			}
			else
			{
				_pos = 0;
				return false;
			}
		}
	};

	
	inline bool MemRFile::CreateFromMemWFile (const SHARED_POINTER_TYPE( BaseMemWFile ) &file, EFlag flag)
	{
		CHECK_ERR( file.IsNotNull() );
		return CreateFromArray( file->_mem, flag );
	}


	
	//
	// Memory Write only File
	//
	
	class MemWFile : public BaseMemWFile
	{
	// types
	public:
		SHARED_POINTER( MemWFile );


	// methods
	public:
		MemWFile () {}


		static MemWFilePtr New (usize reserve = 0)
		{
			MemWFilePtr	file = new MemWFile();

			if ( file->Create( reserve ) )
				return file;

			return null;
		}


		bool Create (usize reserve)
		{
			_mem.ClearMem();
			_mem.Reserve( reserve );

			_pos	= 0;
			_opened	= true;

			return true;
		}


		bool Save (const WFilePtr &file) const
		{
			CHECK_ERR( this != file.ptr() );

			CHECK_ERR( file.IsNotNull() and file->IsOpened() );
			CHECK_ERR( not _mem.Empty() );

			CHECK_ERR( file->Write( _mem.ptr(), _mem.Size() ) );
			return true;
		}


		uni_array<const uint8> GetData () const
		{
			return _mem;
		}
	};


}	// File


namespace UXTypes
{
	typedef SHARED_POINTER_TYPE( File::MemRFile )	MemRFilePtr;
	typedef SHARED_POINTER_TYPE( File::MemWFile )	MemWFilePtr;

}	// UXTypes
}	// UX_STL

#endif	// _UXS_MEMORY_FILE_H